/************************************************************************
 * AIDE/Tools - Automata Identification Engine
 * Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License (LGPL) published 
 * by the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This program is distributed without any
 * warranty. See the GNU Lesser General Public License for more details. 
 * ***********************************************************************
 * File         :     
 * Description  :     
 * Change log   :     	-Date:  Change
 ************************************************************************/
using System;
using SMLib.Automata.Base;
using SMLib.Automata.MM;
using SMLib.Automata.DFA;
using SMLib.Automata.IA;

namespace SysGen
{
	class MainClass
	{
		class Options
		{
			public Options ()
			{
				
			}

			public int OutputStates;
			
			public string ModelType;
			public string ModelName;
			/// <summary>
			/// The size of model
			/// </summary>
			public int Q;
			/// <summary>
			/// The size of input alphabet.
			/// </summary>
			public int SigmaI_size;
			/// <summary>
			/// The size of output alphabet [in MM and IA].
			/// </summary>
			public int SigmaO_size;
			/// <summary>
			/// The acceptanance ratio [in DFAs]
			/// </summary>
			public int AcceptananceRatio;
			/// <summary>
			/// The number of branching states [in MM and IA]
			/// </summary>
			public int numberOfBranching;
			/// <summary>
			/// The degree of non-determinism in branching states [in MM and IA].
			/// </summary>
			public int nonDeterminismDegree;
			public string output;
			public bool help;
		}
		
		private static Options ParseParams (string[] args)
		{
			Options opt = new Options ();
			for (int i=0; i<args.Length; i++) {
				switch (args [i]) {
				case "-help":
					opt.help = true;	
					break;
				case "-Q":
					if (i >= args.Length)
						throw new Exception ("Exception: After -Q parameter, the number of states is expected");
					i++;
					opt.Q = Int32.Parse (args [i]);
					break;
				case "-Si":
					if (i >= args.Length)
						throw new Exception ("Exception: After -Si parameter, the size of input alphabet is expected");
					i++;
					opt.SigmaI_size = Int32.Parse (args [i]);
					break;
				case "-So":
					if (i >= args.Length)
						throw new Exception ("Exception: After -So parameter, the size of output alphabet is expected");
					i++;
					opt.SigmaO_size = Int32.Parse (args [i]);
					break;
				case "-ar":
					if (i >= args.Length)
						throw new Exception ("Exception: After -ar parameter, the size of output alphabet is expected");
					i++;
					//opt. = Int32.Parse (args [i]);
					break;
				case "-nb":
					if (i >= args.Length)
						throw new Exception ("Exception: After -nb parameter, the size of output alphabet is expected");
					i++;
					opt.numberOfBranching = Int32.Parse (args [i]);
					break;
				case "-nd":
					if (i >= args.Length)
						throw new Exception ("Exception: After -nd parameter, the size of output alphabet is expected");
					i++;
					opt.SigmaO_size = Int32.Parse (args [i]);
					break;

				case "-os":
					if (i >= args.Length)
						throw new Exception ("Exception: After -nb parameter, the size of output alphabet is expected");
					i++;
					opt.OutputStates = Int32.Parse (args [i]);
					break;

				case "-t":
					if (i >= args.Length)
						throw new Exception ("Exception: After -t parameter, the type of model is expected");
					i++;
					if (args [i] == "MM")
						opt.ModelType = "MM";
					else if (args [i] == "DFA")
						opt.ModelType = "DFA";
					else if (args [i] == "IA")
						opt.ModelType = "IA";
					else
						throw new Exception ("Undefined type!\n");
					break;
				case "-o":
					if (i >= args.Length)
						throw new Exception ("Exception: After -o parameter, output dir is expected");
					i++;
					opt.output=args[i];
					break;
				}					
			}				
			
			return opt;
		}
		
		private static MealyMachine GenerateRandomMealy (string machineName, int stateSize, int BranchPoints, int BranchDegree, int SigmaILen, int SigmaOLen)
		{
			return MM_Generator.GenerateARandomMachine
				(machineName, stateSize, BranchPoints, BranchDegree, SigmaILen, SigmaOLen);
		}
		
		private static InterfaceAutomaton GenerateRandomIA (string machineName, int stateSize, int SigmaILen, int SigmaOLen, int outputStates)
		{
			return  IA_Generator.GenerateRandomIA(stateSize,outputStates,SigmaILen,SigmaOLen,1,true);
		}
		
		private static DFA GenerateRandomDFA (string machineName, int stateSize,int SigmaILen, int acceptanceRatio)
		{
			DFA_Generator generator=new DFA_Generator(machineName,stateSize,SigmaILen,(double)(acceptanceRatio)/(double)(stateSize));
			return generator.GenerateRandomMachine();
		}
		
		
		public static void Main (string[] args)
		{
			Console.WriteLine("SysGen: Machine generator");
			if(args.Length==0)
			{
				Console.WriteLine ("usage: SysGen [-help] -Q SizeOfStates -Si InputSize -t MachineType [-So OutputSize] ");
			}
			Options opt=ParseParams(args);
			if(opt.help)
			{
				Console.WriteLine ("usage: SysGen [-help] -Q SizeOfStates -Si InputSize -t MachineType [-So OutputSize] ");
				Console.WriteLine("-help. Shows this message");
				Console.WriteLine("-Q SizeOfStates. Number of states in the model");
				Console.WriteLine("-Si InputSize. Size of Input Alphabet");
				Console.WriteLine("-So InputSize. Size of Output Alphabet");
				Console.WriteLine("-t MachineType: Use MM for Mealy Machine, IA for interface automata and DFA for deterministic finite state machines");
				
				return;
			}
			
			if(opt.ModelType=="MM")
			{
				if(opt.Q<=0)
				{
					Console.WriteLine("Number of states should be more than zero!");
					return;
				}
				if(opt.SigmaI_size<1)
				{
					Console.WriteLine("Number of Sigma_I should be more than zero!");
					return;					
				}
				if(opt.SigmaO_size<1)
				{
					Console.WriteLine("Number of Sigma_O should be more than zero!");
					return;					
				}
				if(opt.output==null || opt.output=="")
				{
					opt.output="MealyMachine.dot";
					Console.WriteLine("Using a default path '"+opt.output+"' for model");
				}
				if(opt.ModelName==null || opt.ModelName=="")
				{
					opt.ModelName="Mealy1";
					Console.WriteLine("Using a default name for model");
				}
				Console.WriteLine("Q = "+opt.Q);
				Console.WriteLine("Si = "+opt.SigmaI_size);
				Console.WriteLine("So = "+opt.SigmaO_size);
				MealyMachine m = GenerateRandomMealy(opt.ModelName,opt.Q,opt.numberOfBranching,opt.nonDeterminismDegree,opt.SigmaI_size,opt.SigmaO_size);
				SMLib.Interoperability.DOT.DotHelper_MM.WriteToDot(opt.output,m);
			}
			else if(opt.ModelType=="DFA")
			{				
				if(opt.Q<=0)
				{
					Console.WriteLine("Number of states should be more than zero!");
					return;
				}
				if(opt.SigmaI_size<1)
				{
					Console.WriteLine("Number of Sigma_I should be more than zero!");
					return;					
				}
				if(opt.ModelName==null || opt.ModelName=="")
				{
					opt.ModelName="DFA1";
					Console.WriteLine("Using a default name for model");
				}
				throw new NotImplementedException();
			}
			else if(opt.ModelType=="IA")
			{
				if(opt.Q<=0)
				{
					Console.WriteLine("Number of states should be more than zero!");
					return;
				}
				if(opt.OutputStates==0)
				{
					Console.WriteLine("Number of states with output is defined as zero! I'll use a default value 20%");
					opt.OutputStates=opt.Q/5;
				}
				if(opt.SigmaI_size<1)
				{
					Console.WriteLine("Number of Sigma_I should be more than zero!");
					return;					
				}
				if(opt.SigmaO_size<1)
				{
					Console.WriteLine("Number of Sigma_O should be more than zero!");
					return;					
				}
				if(opt.output==null || opt.output=="")
				{
					opt.output="InterfaceAutomaton1.dot";
					Console.WriteLine("Using a default path '"+opt.output+"' for model");
				}
				if(opt.ModelName==null || opt.ModelName=="")
				{
					opt.ModelName="InterfaceAutomaton1";
					Console.WriteLine("Using a default name for model");
				}
				Console.WriteLine("Q = "+opt.Q);
				Console.WriteLine("Si = "+opt.SigmaI_size);
				Console.WriteLine("So = "+opt.SigmaO_size);
				InterfaceAutomaton ia=GenerateRandomIA(opt.ModelName,opt.Q,opt.SigmaI_size,opt.SigmaO_size,opt.OutputStates);
				SMLib.Interoperability.DOT.DotHelper_IA.WriteToDot(opt.output,ia);
			}
			else
			{
				Console.WriteLine("The model type option should be MM, IA or DFA");
				return;
			}
		}
	}
}
